<?php
namespace common\components\behavior;

use yii\base\Behavior;
use yii\db\ActiveRecord;
use yii\helpers\Json;
use yii\base\InvalidConfigException;

/**
 * ```php
 * public function behaviors()
 * {
 *     return [
 *         [
 *             'class' => common\components\behavior\JsonBehavior::class,
 *             'attributes' => ['attribute1', 'attribute2']
 *         ]
 *     ];
 * }
 * ```
 */
class JsonBehavior extends Behavior
{
    /**
     * @var ActiveRecord
     */
    public $owner;

    /**
     * @var array|string attributes to serialize/deserialize
     */
    public $attributes;

    /**
     * attach
     * @param \yii\base\Component $owner
     * @throws InvalidConfigException
     */
    public function attach($owner)
    {
        parent::attach($owner);

        if (!$this->owner instanceof ActiveRecord) {
            throw new InvalidConfigException('Owner of behavior must be instance of "' . ActiveRecord::class . '".');
        }
    }

    /**
     * init
     * @throws InvalidConfigException
     */
    public function init()
    {
        if (!$this->attributes) {
            throw new InvalidConfigException('The "attributes" property must be set.');
        }

        if (is_string($this->attributes)) {
            $this->attributes = array_map('trim', explode(',', $this->attributes));
        }

        if (!is_array($this->attributes)) {
            throw new InvalidConfigException('The "attributes" property must be string or array.');
        }
    }

    /**
     * events
     * @return array
     */
    public function events()
    {
        return [
            ActiveRecord::EVENT_AFTER_FIND => 'decodeAttributes',
            ActiveRecord::EVENT_AFTER_INSERT => 'decodeAttributes',
            ActiveRecord::EVENT_AFTER_UPDATE => 'decodeAttributes',
            ActiveRecord::EVENT_BEFORE_INSERT => 'encodeAttributes',
            ActiveRecord::EVENT_BEFORE_UPDATE => 'encodeAttributes'
        ];
    }

    /**
     * decodeAttributes
     */
    public function decodeAttributes()
    {
        $this->processModelAttributes([Json::class, 'decode'],[]);
    }

    /**
     * encodeAttributes
     */
    public function encodeAttributes()
    {
        $this->processModelAttributes([Json::class, 'encode'],null);
    }

    /**
     * processModelAttributes
     * @param $callback
     * @param $defaultValue
     */
    private function processModelAttributes($callback,$defaultValue)
    {
        foreach ($this->attributes as $attribute) {
            $value = $this->owner->getAttribute($attribute);
            $this->owner->setAttribute($attribute, !empty($value)?call_user_func($callback, $value):$defaultValue);
        }
    }
}